package org.stvd.controller.workflow;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipInputStream;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.stvd.common.aspectj.lang.annotation.Log;
import org.stvd.common.aspectj.lang.enums.BusinessType;
import org.stvd.common.utils.InterfaceResult;
import org.stvd.common.workflow.WorkflowUtils;
import org.stvd.controller.BaseController;
import org.stvd.core.dto.QueryResult;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

/**
 * @Title DeployController
 * @Destribution 流程部署控制类
 * @author houzx
 * @date 2020年10月27日
 */
@Controller
@RequestMapping(value = "/workflow/deploy")
public class DeployController extends BaseController{

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private RepositoryService repositoryService;

    private String tmpURL = "/workflow/deploy/";
    
    /**
     * 获取已经发布的流程
     */
    @RequestMapping(value = "manage")
    public String getDeployment() {
        List<Object[]> objects = new ArrayList<Object[]>();
        QueryResult<Object[]> queryResult = new QueryResult<Object[]>();
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery().orderByDeploymentId().desc();
        List<ProcessDefinition> processDefinitionList = query.listPage((pageNo - 1)/pageSize, pageSize);
        for (ProcessDefinition processDefinition : processDefinitionList) {
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService.createDeploymentQuery().deploymentId(deploymentId).singleResult();
            objects.add(new Object[]{processDefinition, deployment});
        }
        queryResult.setTotal(query.count());
        queryResult.setRows(objects);
        
        if (query.count() % pageSize == 0) {
            queryResult.setPageCount( (int)(query.count() / pageSize) );
        } else {
            queryResult.setPageCount( (int)(query.count() / pageSize) + 1 );
        }
        
        queryResult.setPageIndex((int) Math.floor((pageNo - 1)/pageSize) + 1);
        if (queryResult.getPageCount() > 0 && queryResult.getPageCount() < queryResult.getPageIndex()) {
            queryResult.setPageIndex(queryResult.getPageIndex() - 1);
        }
        map.put("queryResult", queryResult);
        return tmpURL +"DeployManage.html";
    }

    /**
     * @Destribution 删除部署流程
     * @param deploymentId
     * @return
     * @throws Exception
     */
    @Log(title = "删除部署流程", businessType = BusinessType.DELETE)
    @PostMapping(value = "del")
    @ResponseBody
    public InterfaceResult<Map<String, Object>> onWorkflowDeployDelete(String deploymentId){
        InterfaceResult<Map<String, Object>> result = new InterfaceResult<Map<String,Object>>();
        try {
            WorkflowUtils.delDeployment(deploymentId, true);
            result.setMsg("流程‘" + deploymentId + "’删除成功！");
        } catch (Exception e) {
            e.printStackTrace();
            result.setError("删除流程" + deploymentId + "失败！<br/>失败原因：" + e.getMessage());
        }
        return result;
    }
    
    /**
     * @Destribution 转换为模型
     * @param processDefinitionId
     * @param redirectAttributes
     * @return
     * @throws UnsupportedEncodingException
     * @throws XMLStreamException
     */
    @Log(title = "转换流程为模型", businessType = BusinessType.UPDATE)
    @PostMapping(value = "converttomodel")
    @ResponseBody
    public InterfaceResult<Map<String, Object>> workflowConvertToModel(
        String processDefinitionId, RedirectAttributes redirectAttributes) {
        InterfaceResult<Map<String, Object>> result = new InterfaceResult<Map<String,Object>>();
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionId(processDefinitionId).singleResult();
            InputStream bpmnStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                    processDefinition.getResourceName());
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStreamReader in = new InputStreamReader(bpmnStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
    
            BpmnJsonConverter converter = new BpmnJsonConverter();
            com.fasterxml.jackson.databind.node.ObjectNode modelNode = converter.convertToJson(bpmnModel);
            Model modelData = repositoryService.newModel();
            modelData.setKey(processDefinition.getKey());
            modelData.setName(processDefinition.getResourceName());
            modelData.setCategory(processDefinition.getDeploymentId());
    
            ObjectNode modelObjectNode = new ObjectMapper().createObjectNode();
            modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
            modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
            modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
            modelData.setMetaInfo(modelObjectNode.toString());
    
            repositoryService.saveModel(modelData);
            repositoryService.addModelEditorSource(modelData.getId(), modelNode.toString().getBytes("utf-8"));
            redirectAttributes.addFlashAttribute("message", "Model转换成功！");
            result.setMsg("Model转换成功！");
        } catch (UnsupportedEncodingException e) {
            result.setError("Model转换失败！失败原因："+e.getMessage());
            e.printStackTrace();
        } catch (XMLStreamException e) {
            result.setError("Model转换失败！失败原因："+e.getMessage());
            e.printStackTrace();
        }
        return result;
    }
    
    /**
     * 挂起、激活流程实例
     */
    @Log(title = "流程部署状态变更", businessType = BusinessType.UPDATE)
    @PostMapping(value = "processdefinition/update")
    @ResponseBody
    public InterfaceResult<Map<String, Object>> updateState(String state, String processDefinitionId,
        RedirectAttributes redirectAttributes) {
        InterfaceResult<Map<String, Object>> result = new InterfaceResult<Map<String,Object>>();
        if (state.equals("active")) {
            repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
            redirectAttributes.addFlashAttribute("message", "已激活ID为[" + processDefinitionId + "]的流程！");
            result.setMsg("已激活ID为[" + processDefinitionId + "]的流程！");
        } else if (state.equals("suspend")) {
            repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
            redirectAttributes.addFlashAttribute("message", "已挂起ID为[" + processDefinitionId + "]的流程定！");
            result.setMsg("已挂起ID为[" + processDefinitionId + "]的流程！");
        }
        return result;
    }

    /**
     * @Destribution 新增流程部署 
     * @return
     */
    @GetMapping(value = "upload")
    public String workflowDeployAddLoad(){
        return tmpURL + "DeployUpload.html";  
    }
    
    @Log(title = "上传流程部署", businessType = BusinessType.INSERT)
    @PostMapping(value = "upload")
    @ResponseBody
    public InterfaceResult<Map<String, Object>> workflowDeployAddAction(
        @RequestParam(value = "file") MultipartFile file,
        RedirectAttributes redirectAttributes) {
        InterfaceResult<Map<String, Object>> result = new InterfaceResult<Map<String,Object>>();
        String fileName = file.getOriginalFilename();
        try {
            InputStream fileInputStream = file.getInputStream();
            String extension = FilenameUtils.getExtension(fileName);
            if (extension.equals("zip") || extension.equals("bar")) {
                ZipInputStream zip = new ZipInputStream(fileInputStream);
                repositoryService.createDeployment().addZipInputStream(zip).deploy();
            } else {
                repositoryService.createDeployment().addInputStream(fileName, fileInputStream).deploy();
            }
            redirectAttributes.addFlashAttribute("message", "流程部署成功！");
            result.setMsg("流程部署成功！");
        } catch (Exception e) {
            result.setMsg("流程部署失败！失败原因：" +e.getMessage());
            logger.error("error on deploy process, because of file input stream", e);
        }
        return result;
    }
}
